]> dgit.raspbian.org Git - gnome-shell.git/commitdiff
st/theme-node-drawing: Factor in border radius in update_resources()
authorJonas Dreßler <verdre@v0yd.nl>
Thu, 6 Jun 2024 15:13:27 +0000 (18:13 +0300)
committerMarge Bot <marge-bot@gnome.org>
Fri, 21 Jun 2024 15:32:34 +0000 (15:32 +0000)
The reduced border radius from st_theme_node_reduce_border_radius() depends
on the allocation size, and when it changes, has_large_corners can change.
has_large_corners is used to determine whether to pre-render using cairo,
so that means a change to the allocation size can influence whether the
pre-render using cairo should happen or not.

On changes to the allocation size we don't want to call render_resources()
though, but just update_resources() instead. And since we know now that
changes to the allocation size might cause pre-rendering to get enabled
or disabled, we need to do the "should pre-render" checks in
update_resources(), too.

So factor out those "should pre-render" checks into a new function
st_theme_node_maybe_prerender_background(), and then call that function from
both render_resources() and update_resources().

This fixes rendering artifacts with borders or rounded corners after changes
to the actor size.

Closes: https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/6567
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/3354>

src/st/st-theme-node-drawing.c

index 774544cc0e05c45f5937d6e64248bd258107161c..58b074eea9732c405af505f5fba331d9544a6b41 100644 (file)
@@ -1362,6 +1362,92 @@ st_theme_node_prerender_background (StThemeNode *node,
   return texture;
 }
 
+static void
+st_theme_node_maybe_prerender_background (StThemeNodePaintState *state,
+                                          StThemeNode           *node,
+                                          float                  width,
+                                          float                  height,
+                                          float                  resource_scale)
+{
+  gboolean has_border;
+  gboolean has_border_radius;
+  gboolean has_inset_box_shadow;
+  gboolean has_large_corners;
+  StShadow *box_shadow_spec;
+
+  box_shadow_spec = st_theme_node_get_box_shadow (node);
+
+  has_inset_box_shadow = box_shadow_spec && box_shadow_spec->inset;
+
+  if (node->border_width[ST_SIDE_TOP] > 0 ||
+      node->border_width[ST_SIDE_LEFT] > 0 ||
+      node->border_width[ST_SIDE_RIGHT] > 0 ||
+      node->border_width[ST_SIDE_BOTTOM] > 0)
+    has_border = TRUE;
+  else
+    has_border = FALSE;
+
+  if (node->border_radius[ST_CORNER_TOPLEFT] > 0 ||
+      node->border_radius[ST_CORNER_TOPRIGHT] > 0 ||
+      node->border_radius[ST_CORNER_BOTTOMLEFT] > 0 ||
+      node->border_radius[ST_CORNER_BOTTOMRIGHT] > 0)
+    has_border_radius = TRUE;
+  else
+    has_border_radius = FALSE;
+
+  /* The cogl code pads each corner to the maximum border radius,
+   * which results in overlapping corner areas if the radius
+   * exceeds the actor's halfsize, causing rendering errors.
+   * Fall back to cairo in these cases. */
+  has_large_corners = FALSE;
+
+  if (has_border_radius) {
+    guint border_radius[4];
+    int corner;
+
+    st_theme_node_reduce_border_radius (node, width, height, border_radius);
+
+    for (corner = 0; corner < 4; corner ++) {
+      if (border_radius[corner] * 2 > height ||
+          border_radius[corner] * 2 > width) {
+        has_large_corners = TRUE;
+        break;
+      }
+    }
+  }
+
+  state->corner_material[ST_CORNER_TOPLEFT] =
+    st_theme_node_lookup_corner (node, width, height, resource_scale, ST_CORNER_TOPLEFT);
+  state->corner_material[ST_CORNER_TOPRIGHT] =
+    st_theme_node_lookup_corner (node, width, height, resource_scale, ST_CORNER_TOPRIGHT);
+  state->corner_material[ST_CORNER_BOTTOMRIGHT] =
+    st_theme_node_lookup_corner (node, width, height, resource_scale, ST_CORNER_BOTTOMRIGHT);
+  state->corner_material[ST_CORNER_BOTTOMLEFT] =
+    st_theme_node_lookup_corner (node, width, height, resource_scale, ST_CORNER_BOTTOMLEFT);
+
+  /* Use cairo to prerender the node if there is a gradient, or
+   * background image with borders and/or rounded corners,
+   * or large corners, since we can't do those things
+   * easily with cogl.
+   *
+   * FIXME: if we could figure out ahead of time that a
+   * background image won't overlap with the node borders,
+   * then we could use cogl for that case.
+   */
+  if ((node->background_gradient_type != ST_GRADIENT_NONE)
+      || (has_inset_box_shadow && (has_border || node->background_color.alpha > 0))
+      || (st_theme_node_get_background_image (node) && (has_border || has_border_radius))
+      || has_large_corners)
+    {
+      state->prerendered_texture = st_theme_node_prerender_background (node, width, height, resource_scale);
+
+      if (state->prerendered_texture)
+        state->prerendered_pipeline = _st_create_texture_pipeline (state->prerendered_texture);
+      else
+        state->prerendered_pipeline = NULL;
+    }
+}
+
 static void st_theme_node_paint_borders (StThemeNodePaintState *state,
                                          ClutterPaintNode      *node,
                                          const ClutterActorBox *box,
@@ -1487,10 +1573,7 @@ st_theme_node_render_resources (StThemeNodePaintState *state,
                                 float                  height,
                                 float                  resource_scale)
 {
-  gboolean has_border;
-  gboolean has_border_radius;
   gboolean has_inset_box_shadow;
-  gboolean has_large_corners;
   StShadow *box_shadow_spec;
 
   g_return_if_fail (width > 0 && height > 0);
@@ -1512,72 +1595,7 @@ st_theme_node_render_resources (StThemeNodePaintState *state,
   box_shadow_spec = st_theme_node_get_box_shadow (node);
   has_inset_box_shadow = box_shadow_spec && box_shadow_spec->inset;
 
-  if (node->border_width[ST_SIDE_TOP] > 0 ||
-      node->border_width[ST_SIDE_LEFT] > 0 ||
-      node->border_width[ST_SIDE_RIGHT] > 0 ||
-      node->border_width[ST_SIDE_BOTTOM] > 0)
-    has_border = TRUE;
-  else
-    has_border = FALSE;
-
-  if (node->border_radius[ST_CORNER_TOPLEFT] > 0 ||
-      node->border_radius[ST_CORNER_TOPRIGHT] > 0 ||
-      node->border_radius[ST_CORNER_BOTTOMLEFT] > 0 ||
-      node->border_radius[ST_CORNER_BOTTOMRIGHT] > 0)
-    has_border_radius = TRUE;
-  else
-    has_border_radius = FALSE;
-
-  /* The cogl code pads each corner to the maximum border radius,
-   * which results in overlapping corner areas if the radius
-   * exceeds the actor's halfsize, causing rendering errors.
-   * Fall back to cairo in these cases. */
-  has_large_corners = FALSE;
-
-  if (has_border_radius) {
-    guint border_radius[4];
-    int corner;
-
-    st_theme_node_reduce_border_radius (node, width, height, border_radius);
-
-    for (corner = 0; corner < 4; corner ++) {
-      if (border_radius[corner] * 2 > height ||
-          border_radius[corner] * 2 > width) {
-        has_large_corners = TRUE;
-        break;
-      }
-    }
-  }
-
-  state->corner_material[ST_CORNER_TOPLEFT] =
-    st_theme_node_lookup_corner (node, width, height, resource_scale, ST_CORNER_TOPLEFT);
-  state->corner_material[ST_CORNER_TOPRIGHT] =
-    st_theme_node_lookup_corner (node, width, height, resource_scale, ST_CORNER_TOPRIGHT);
-  state->corner_material[ST_CORNER_BOTTOMRIGHT] =
-    st_theme_node_lookup_corner (node, width, height, resource_scale, ST_CORNER_BOTTOMRIGHT);
-  state->corner_material[ST_CORNER_BOTTOMLEFT] =
-    st_theme_node_lookup_corner (node, width, height, resource_scale, ST_CORNER_BOTTOMLEFT);
-
-  /* Use cairo to prerender the node if there is a gradient, or
-   * background image with borders and/or rounded corners,
-   * or large corners, since we can't do those things
-   * easily with cogl.
-   *
-   * FIXME: if we could figure out ahead of time that a
-   * background image won't overlap with the node borders,
-   * then we could use cogl for that case.
-   */
-  if ((node->background_gradient_type != ST_GRADIENT_NONE)
-      || (has_inset_box_shadow && (has_border || node->background_color.alpha > 0))
-      || (st_theme_node_get_background_image (node) && (has_border || has_border_radius))
-      || has_large_corners)
-    state->prerendered_texture = st_theme_node_prerender_background (node, width, height,
-                                                                     resource_scale);
-
-  if (state->prerendered_texture)
-    state->prerendered_pipeline = _st_create_texture_pipeline (state->prerendered_texture);
-  else
-    state->prerendered_pipeline = NULL;
+  st_theme_node_maybe_prerender_background (state, node, width, height, resource_scale);
 
   if (box_shadow_spec && !has_inset_box_shadow)
     {
@@ -1616,14 +1634,12 @@ st_theme_node_update_resources (StThemeNodePaintState *state,
                                 float                  height,
                                 float                  resource_scale)
 {
-  gboolean had_prerendered_texture = FALSE;
   gboolean had_box_shadow = FALSE;
   StShadow *box_shadow_spec;
 
   g_return_if_fail (width > 0 && height > 0);
 
   /* Free handles we can't reuse */
-  had_prerendered_texture = (state->prerendered_texture != NULL);
   g_clear_object (&state->prerendered_texture);
 
   if (state->prerendered_pipeline != NULL)
@@ -1645,20 +1661,7 @@ st_theme_node_update_resources (StThemeNodePaintState *state,
 
   box_shadow_spec = st_theme_node_get_box_shadow (node);
 
-  if (had_prerendered_texture)
-    {
-      state->prerendered_texture = st_theme_node_prerender_background (node, width, height, resource_scale);
-      state->prerendered_pipeline = _st_create_texture_pipeline (state->prerendered_texture);
-    }
-  else
-    {
-      int corner_id;
-
-      for (corner_id = 0; corner_id < 4; corner_id++)
-        if (state->corner_material[corner_id] == NULL)
-          state->corner_material[corner_id] =
-            st_theme_node_lookup_corner (node, width, height, resource_scale, corner_id);
-    }
+  st_theme_node_maybe_prerender_background (state, node, width, height, resource_scale);
 
   if (had_box_shadow)
     state->box_shadow_pipeline = _st_create_shadow_pipeline (box_shadow_spec,